
// ===============================================================================================================
// -*- C++ -*-
//
// Doom 3 MD5.hpp - Classes and data structures to support loading and use of the Doom 3 MD5 models.
//
// Copyright (c) 2005-2007 David Henry
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// Modified by: Guilherme R. Lampert - March 2011
// guilherme.ronaldo.lampert@gmail.com
//
// ===============================================================================================================

#ifndef __DOOM_3_MD5_HPP__
#define __DOOM_3_MD5_HPP__

// C++ std includes
#include <string>
#include <hash_map>

// Local includes
#include <Vector.hpp>
#include <Quaternion.hpp>
#include <GeoUtils.hpp>

#include <Common.hpp>
#include <Texture.hpp>
#include <MemoryBuffer.hpp>

#include <RefPtr.hpp>
#include <ReferenceCountable.hpp>

// ===============================================================================================================

// Doom 3 MD5 Model And Animation Format Data Structures //

///
/// MD5 Mesh Joint
///
struct DoomMD5Joint {

	char name[64];
	int parent;

	Vec3 pos;
	Quaternion orient;
};

///
/// MD5 Mesh Vertex
///
struct DoomMD5Vertex {

	float st[2]; // Texture coordinates
	int start;   // Start weight
	int count;   // Weight count
};

///
/// MD5 Mesh Triangle
///
struct DoomMD5Triangle {

	int index[3]; // Vertex indices
};

///
/// MD5 Mesh Weight
///
struct DoomMD5Weight {

	int joint;
	float bias;

	Vec3 pos;
};

///
/// MD5 Mesh
///
struct DoomMD5Mesh {

	DoomMD5Vertex * vertices;
	DoomMD5Triangle * triangles;
	DoomMD5Weight * weights;

	int numVerts;
	int numTris;
	int numWeights;

	Texture * textureObject;
	char shader[256]; // Texture image for this mesh. (Without the file extension)
};

// ===============================================================================================================

///
/// DoomMD5Anim -- Class representing an animation set of a Doom 3 model.
/// The animation data is loaded from a .md5anim file by the DoomMD5Factory.
///
class DoomMD5Anim : public ReferenceCountable {

	friend class DoomMD5Factory;
	friend class DoomMD5Model; // For performace reasons I want direct access to the animation data from the DoomMD5Model.

public:

	// Public Interface:

	int GetNumFrames(void) const;
	int GetNumJoints(void) const;
	int GetFrameRate(void) const;

	DoomMD5Joint ** GetFrames(void) const;
	BoundingBox * GetFramesBoundingBoxes(void) const;

	float GetPlaybackTime(void) const; // Playback time in seconds.

	// ReferenceCountable Methods:

	virtual unsigned long AddRef(void) const;
	virtual unsigned long Release(void) const;
	virtual unsigned long ReferenceCount(void) const;

private:

	// We only load from memory!
	DoomMD5Anim(MemoryBuffer * memory);

	// Not allowed to be directly deleted. Use the Release() Method!
	virtual ~DoomMD5Anim(void);

	// Helper Structs:

	struct JointInfo {

		char name[64];
		int parent;
		int flags;
		int startIndex;
	};

	struct BaseFrameJoint {

		Vec3 pos;
		Quaternion orient;
	};

	// Private Methods:

	/// Build skeleton for a given frame data.
	static void BuildFrameSkeleton(const JointInfo * jointInfos, const BaseFrameJoint * baseFrame,
								   const float * animFrameData, DoomMD5Joint * skelFrame, int numberOfJoints);

private:

	// Animation Data:

	DoomMD5Joint ** skelFrames;
	BoundingBox * bboxes;

	int numFrames;
	int numJoints;
	int frameRate;

	// Time / Frame Index Data:

	int currFrame;
	int nextFrame;

	float lastTime;
	float maxTime;
};

///
/// Reference countable DoomMD5Anim pointer type.
///
typedef RefPtr<DoomMD5Anim> DoomMD5AnimPtr;

// ===============================================================================================================

///
/// DoomMD5Model -- A game model object loaded from a Doom 3 .md5mesh file.
/// It contains the meshes, textures ans animations that compose the object.
/// Create an instace of it thru the DoomMD5Factory and use the Renderer to draw it to screen.
///
class DoomMD5Model : public ReferenceCountable {

	friend class DoomMD5Factory;
	friend class Renderer; // Once again, for performace we allow the renderer to directly read the model data.

public:

	// Public Interface:

	/// Allocate the shared arrays for all models.
	/// Call this method once before any model is rendered and after all models have been loaded.
	static void AllocSharedArrays(void);

	/// Free the shared arrays created by AllocVertexArrays().
	static void FreeSharedArrays(void);

	/// Add new animation to the model animation list. The new animation can be refered to by its name.
	/// Only a reference to the animation is kept, reference counting ensures memory will stay valid.
	bool RegisterAnimation(DoomMD5Anim * newAnim, const std::string & animName);

	/// Remove an animation from the animation list.
	void UnregisterAnimation(const std::string & animName);

	/// Perform animation related computations. Calculate the current and next frames, given a delta time.
	/// \return A value grater than zero if the animation has reached its end and will start a new loop.
	/// A value equal to zero if an entire loop was not yeat performed.
	int Animate(DoomMD5Anim * anim, float elapsedTime);

	// Data Access:

	int GetNumMeshes(void) const;
	DoomMD5Mesh * GetMesh(int which) const;

	int GetNumJoints(void) const;
	DoomMD5Joint * GetJoint(int which) const;

	int GetNumAnimations(void) const;
	DoomMD5Anim * GetAnimation(const std::string & animName) const; // Call Release() on the anim when done!

	// ReferenceCountable Methods:

	virtual unsigned long AddRef(void) const;
	virtual unsigned long Release(void) const;
	virtual unsigned long ReferenceCount(void) const;

private:

	// We only load from memory!
	DoomMD5Model(MemoryBuffer * memory);

	// Not allowed to be directly deleted. Use the Release() Method!
	virtual ~DoomMD5Model(void);

	/// Prepare a mesh for drawing. Compute mesh's final vertex positions
	/// given a skeleton. Put the vertices in the vertex array.
	static void PrepareMesh(const DoomMD5Mesh * mesh, const DoomMD5Joint * skeleton);

	/// Smoothly interpolate two skeletons.
	static void InterpolateSkeletons(const DoomMD5Joint * skelA, const DoomMD5Joint * skelB,
									 int numberOfJoints, float interp, DoomMD5Joint * outSkel);

private:

	// MD5 Model Data:

	DoomMD5Joint * baseSkel;
	DoomMD5Mesh  * meshes;

	int numJoints;
	int numMeshes;

	// Once again I use the stdext::hash_map, in case your compiler does not support it, just replace it with the std::map.
	typedef stdext::hash_map<const std::string, DoomMD5Anim *> AnimationMap;
	AnimationMap animMap;

private:

	// Shared Rendering Data:

	// Implementation Note:
	// This static arrays are shared among all MD5 meshes.
	// This allows for a good memory saving, because the model vertices have
	// to be recalculated every frame in order to animate it propertly,
	// so there is no need to have one vertex/index array per model. This is a problem however
	// when working with multithreaded applications, in such a case, per instance arrays are better.
	// Anyway, a good optimization here would be to calculate the final interpolated vertex positions in the
	// GPU, on-the-fly right before drawing the model.

	static unsigned int * indexArray;
	static int numIndices;

	static Vec3 * vertexArray;
	static int numVertices;

	static Vec2 * texCoordArray;
};

///
/// Reference countable DoomMD5Model pointer type.
///
typedef RefPtr<DoomMD5Model> DoomMD5ModelPtr;

// ===============================================================================================================

///
/// DoomMD5Factory -- A factory class for the Doom 3 MD5 models and animations.
///
/// This class is not instantiable, it has all its methods declared as static.
///
class DoomMD5Factory {

public:

	// Animations:

	/// Creates an animation from a .md5anim file.
	static DoomMD5Anim * CreateAnimFromFile(const std::string & fileName);

	/// Creates a model from a .md5anim file loaded into memory.
	static DoomMD5Anim * CreateAnimFromMemory(MemoryBuffer * memory, const std::string & originalFile);

	// Models:

	/// Creates a model from a .md5mesh file.
	static DoomMD5Model * CreateModelFromFile(const std::string & fileName);

	/// Creates a model from a .md5mesh file loaded into memory.
	static DoomMD5Model * CreateModelFromMemory(MemoryBuffer * memory, const std::string & originalFile);

private:

	DoomMD5Factory(void); // Leave it private and unimplemented.
	DoomMD5Factory & operator = (const DoomMD5Factory &);
};

#endif // __DOOM_3_MD5_HPP__